package com.bingxue.edu.management.system.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.util.ObjectUtil;
import com.bingxue.edu.common.enums.CommonStatusEnum;
import com.bingxue.edu.common.enums.OrgTypeEnum;
import com.bingxue.edu.management.system.mapper.OrgMapper;
import com.bingxue.edu.management.system.model.entity.Org;
import com.bingxue.edu.management.system.model.req.OrgReq;
import com.bingxue.edu.management.system.model.req.OrgSortReq;
import com.bingxue.edu.management.system.model.resp.OrgResp;
import com.bingxue.edu.management.system.model.resp.OrgSummaryResp;
import com.bingxue.edu.framework.common.util.CheckUtils;
import com.bingxue.edu.framework.common.util.TreeUtils;
import com.bingxue.edu.framework.common.util.ValidationUtils;
import com.bingxue.edu.framework.mybatis.base.BaseService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

import static com.bingxue.edu.common.constant.AuthConstants.ROOT_ID;
import static com.bingxue.edu.management.system.model.entity.table.OrgTableDef.ORG;
import static com.mybatisflex.core.query.QueryMethods.string;


/**
 * 组织业务实现
 *
 * @author panbo
 * @since 2024/07/09 19:52
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrgService extends BaseService<OrgMapper, Org> {

    private final ObjectMapper objectMapper;
    private final EmployeeService employeeService;

    /**
     * 查询组织树
     */
    public List<Tree<String>> tree(QueryWrapper queryWrapper) {
        List<OrgResp> orgList = this.mapper.selectListWithRelationsByQueryAs(queryWrapper, OrgResp.class);

        List<TreeNode<String>> treeNodeList = orgList.stream().map(item ->
                new TreeNode<String>()
                        .setId(item.getId())
                        .setName(item.getName())
                        .setParentId(item.getParentId())
                        .setWeight(item.getSort())
                        .setExtra(objectMapper.convertValue(item, new TypeReference<>() {}))
        ).toList();
        return TreeUtils.build(treeNodeList, "");
    }

    /**
     * 查询总览数据
     * @param id 组织id
     */
    public OrgSummaryResp getSummary(String id) {
//        QueryWrapper queryWrapper = QueryWrapper.create().select().from(ORG).where(ORG.ID.eq(id));
//        QueryWrapper queryWrapper = QueryWrapper.create().select().from(EMPLOYEE).where(ORG.ID.eq(id));
//        mapper.selectListByQuery(queryWrapper, fieldQueryBuilder -> fieldQueryBuilder
//                .field(OrgSummaryResp::getEmployeeCount)
//
//                .queryWrapper(item -> QueryWrapper.create().select(count))
//
//        )
        return null;
    }


    /**
     * 创建组织
     * 同一级下组织不可重名, 不同级下可以重名
     */
    public void createOrg(OrgReq req) {
        boolean isExists = this.isNameExists(req.getName(), req.getParentId(), null);
        ValidationUtils.throwIf(isExists, "[{}]已存在", req.getName());
        Org newOrg = this.copy(req);
        newOrg.setAncestors(this.getAncestors(req.getParentId()));
        this.save(newOrg);
    }

    public void updateOrg(OrgReq req, String id) {
        String name = req.getName();
        boolean isExists = this.isNameExists(name, req.getParentId(), id);
        ValidationUtils.throwIf(isExists, "修改失败，[{}] 已存在", name);
        Org oldOrg = super.getById(id);
        String oldName = oldOrg.getName();
        String oldParentId = oldOrg.getParentId();
        CommonStatusEnum newStatus = req.getStatus();
        if (id.equals(ROOT_ID)) {
            ValidationUtils.throwIfEqual(CommonStatusEnum.DISABLED, newStatus, "[{}] 是系统内置组织，不允许禁用", oldName);
            ValidationUtils.throwIfNotEqual(req.getParentId(), oldParentId, "[{}] 是系统内置组织，不允许变更上级组织", oldName);
        }
        // 启用/禁用部门
        if (ObjectUtil.notEqual(newStatus, oldOrg.getStatus())) {
            List<Org> children = this.listChildren(id);
            long enabledChildrenCount = children.stream()
                    .filter(d -> CommonStatusEnum.ENABLED.equals(d.getStatus()))
                    .count();
            CheckUtils.throwIf(CommonStatusEnum.DISABLED
                    .equals(newStatus) && enabledChildrenCount > 0, "禁用 [{}] 前，请先禁用其所有下级部门", oldName);
            Org oldParentOrg = this.getByParentId(oldParentId);
            CheckUtils.throwIf(CommonStatusEnum.ENABLED.equals(newStatus) && CommonStatusEnum.DISABLED
                    .equals(oldParentOrg.getStatus()), "启用 [{}] 前，请先启用其所有上级部门", oldName);
        }
        // 变更上级部门
        Org org = this.copyById(req, id);
        if (ObjectUtil.notEqual(req.getParentId(), oldParentId)) {
            // 更新祖级列表
            String newAncestors = this.getAncestors(req.getParentId());
            org.setAncestors(newAncestors);
            // 更新子级的祖级列表
            this.updateChildrenAncestors(newAncestors, oldOrg.getAncestors(), id);
        }
        this.updateById(org);
    }

    /**
     * 删除组织
     * 只删除不含子组织和员工的组织
     * 要么全删除,要么全不删除
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeOrgByIds(List<String> ids) {
        ValidationUtils.throwIf(ids.contains(ROOT_ID), "不能删除系统内置组织");
        ValidationUtils.throwIf(this.countChildren(ids) > 0, "所选组织存在下级组织，不允许删除");
        ValidationUtils.throwIf(employeeService.countByOrgIds(ids) > 0, "所选部门存在用户关联，请解除关联后重试");
        this.removeByIds(ids);
    }

    /**
     * 查询子组织列表
     */
    public List<Org> listChildren(String id) {
        return this.queryChain().where(QueryMethods.findInSet(string(id), ORG.ANCESTORS).ne(0)).list();
    }

    /**
     * 获取祖级列表
     *
     * @param parentId 上级部门
     * @return 祖级列表
     */
    private String getAncestors(String parentId) {
        Org parentOrg = this.getByParentId(parentId);
        return "%s,%s".formatted(parentOrg.getAncestors(), parentId);
    }

    /**
     * 根据上级部门 ID 查询
     *
     * @param parentId 上级部门 ID
     * @return 上级部门信息
     */
    private Org getByParentId(String parentId) {
        Org parent = getById(parentId);
        CheckUtils.throwIfNull(parent, "上级组织不存在");
        return parent;
    }

    /**
     * 名称是否存在
     *
     * @param name     名称
     * @param parentId 上级 ID
     * @param selfId   自身 ID
     * @return 是否存在
     */
    private boolean isNameExists(String name, String parentId, String selfId) {
        return this.queryChain()
                .where(ORG.NAME.eq(name))
                .and(ORG.PARENT_ID.eq(parentId))
                .and(ORG.ID.ne(selfId))
                .exists();
    }

    /**
     * 查询子部门数量
     *
     * @param ids ID 列表
     * @return 子部门数量
     */
    private long countChildren(List<String> ids) {
        if (CollUtil.isEmpty(ids)) {
            return 0;
        }
        return ids.stream()
                .mapToLong(id -> this.queryChain().where(QueryMethods.findInSet(string(id), ORG.ANCESTORS).ne(0)).count())
                .sum();
    }

    /**
     * 更新子部门祖级列表
     *
     * @param newAncestors 新祖级列表
     * @param oldAncestors 原祖级列表
     * @param id           ID
     */
    private void updateChildrenAncestors(String newAncestors, String oldAncestors, String id) {
        List<Org> children = this.listChildren(id);
        if (CollUtil.isEmpty(children)) {
            return;
        }
        List<Org> list = new ArrayList<>(children.size());
        for (Org child : children) {
            Org dept = new Org();
            dept.setId(child.getId());
            dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
            list.add(dept);
        }
        this.updateBatch(list);
    }

    /**
     * 排序组织
     */
    @Transactional(rollbackFor = Exception.class)
    public void sortOrg(OrgSortReq req) {
        int sort = 1;
        for (String childId : req.getChildrenIds()) {
            this.updateChain().where(ORG.ID.eq(childId)).set(ORG.SORT, sort).update();
            sort++;
        }
    }

    /**
     * 获取校区列表
     */
    public List<Org> getCampusList() {
        return  queryChain()
                .where(ORG.ORG_TYPE.eq(OrgTypeEnum.CAMPUS))
                .and(ORG.STATUS.eq(CommonStatusEnum.ENABLED))
                .list();
    }
}
